#pragma once
#ifndef __OUTPUT_FORWARD_TABLE_HPP__
#define __OUTPUT_FORWARD_TABLE_HPP__

#include <app/VirtualClient.h>
#include <stdint.h>
#include <unordered_map>
#include <unordered_set>

struct output_forward_rule {
    uint32_t user;
    uint32_t host;
    uint32_t rule_key;

    void transform_to_network_byte_order() {

    }
    void transform_to_host_byte_order() {

    }

    bool operator==(output_forward_rule const& rule) const noexcept{
        return user == rule.user &&
               host == rule.host &&
               rule_key == rule.rule_key;
    }
};
template<>
struct std::hash<output_forward_rule>{
    size_t operator()(output_forward_rule const& f) const noexcept{
        constexpr uint16_t MARK = 0b0000'0011'1111'1111;
        uint32_t code = ((f.user & MARK) << 20) 
                      | ((f.host & MARK) << 10)
                      | (f.rule_key & MARK);
        return std::hash<uint32_t>()(code);
    }
};

struct ip_port{
    uint32_t ip;
    uint16_t port;
};

struct output_forward_rule_app{
    std::unordered_set<std::shared_ptr<VirtualClient>> clients;
};

struct output_forward_table{
    std::unordered_map<output_forward_rule, ip_port> rule_to_key;
    std::unordered_map<output_forward_rule, output_forward_rule_app> apps;

    bool contain_rule(output_forward_rule const& rule){
        return rule_to_key.count(rule);
    }

    ip_port get_ip_port(output_forward_rule const& rule){
        return rule_to_key.at(rule);
    }

    void put_rule(output_forward_rule const& rule, ip_port addr){
        rule_to_key.emplace(rule, addr);
    }

    void rm_rule(output_forward_rule const& rule){
        rule_to_key.erase(rule);
    }

    output_forward_rule_app get_apps(output_forward_rule const& rule){
        return apps.at(rule);
    }

    void add_apps(output_forward_rule const& rule, std::shared_ptr<VirtualClient> client){
        apps[rule].clients.insert(client);
    }

    void rm_apps(output_forward_rule const& rule, std::shared_ptr<VirtualClient> client){
        apps[rule].clients.erase(client);
    }

};

#endif // __OUTPUT_FORWARD_TABLE_HPP__